package com.aaron.platform.dao;

import java.io.Serializable;
import java.util.List;
import org.hibernate.FetchMode;
import org.hibernate.criterion.CriteriaSpecification;

import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Expression;
import org.hibernate.criterion.Restrictions;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

public abstract class GenericDAO<T, ID extends Serializable> extends HibernateDaoSupport
        implements IGenericDAO<T, ID> {

    private Class<T> entityClass;

    public Class<T> getPersistentClass() {
        return entityClass;
    }

    public GenericDAO(Class<T> persistentClass) {
        this.entityClass = persistentClass;
    }

    public GenericDAO() {
    }

    @SuppressWarnings("unchecked")
    public T findById(ID id) {
        return (T) getHibernateTemplate().load(entityClass, id);
    }

    public void remove(T entity) {
        getHibernateTemplate().delete(entity);
    }

    public void removeById(ID id) {
        T entity = findById(id);
        if (entity != null) {
            getHibernateTemplate().delete(entity);
        }
    }

    public void removeAll(List<T> entities) {
        getHibernateTemplate().deleteAll(entities);
    }

    public void save(T entity) {
        try {
            getHibernateTemplate().save(entity);
            getHibernateTemplate().initialize(entity);
        } catch (RuntimeException re) {
            //log
            throw re;
        }
    }

    public void saveOrUpdateAll(List<T> entities) {
        getHibernateTemplate().saveOrUpdateAll(entities);
    }

    public void saveOrUpdate(T entity) {
        getHibernateTemplate().saveOrUpdate(entity);
    }

    public void update(T entity) {
        try {
            getHibernateTemplate().update(entity);
        } catch (RuntimeException re) {
            //log
            throw re;
        }
    }

     public void merge(T entity) {
        try {
            getHibernateTemplate().merge(entity);
        } catch (RuntimeException re) {
            //log
            throw re;
        }
    }

    public T findByUniqueKey(String keyName, Serializable key) {
        //log.debug("read instance by uniquekey - " + keyName + ": " + key);
        try {
            //System.out.println("Aaron: the entity class is :" + this.entityClass);
                DetachedCriteria detachedCriteria = DetachedCriteria.forClass(this.entityClass);
            // log.debug("this.objectClass:" + this.objectClass);
            detachedCriteria = detachedCriteria.add(Expression.eq(keyName, key));
            detachedCriteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
            List<T> list = this.getHibernateTemplate().findByCriteria(
                    detachedCriteria);

            // log.debug("list:" + list);
            if (list.size() == 0) {
                //log.error("not found!");
                return null;
            } else if (list.size() > 1) {
                throw new RuntimeException("RuntimeException:NotUniqueKey");
            }
            else {
                return list.get(0);
            }
        } catch (RuntimeException e) {
            //log.error("readByUniqueKey failed", e);
            throw e;
        }
    }

    public List<T> findByProperty(String propertyName, Object value) {
        //log.debug("finding User instance with property: " + propertyName + ", value: " + value);
        try {
            DetachedCriteria detachedCriteria = DetachedCriteria.forClass(this.entityClass);
            // log.debug("this.objectClass:" + this.objectClass);
            detachedCriteria = detachedCriteria.add(Expression.eq(propertyName, value));
            detachedCriteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
            List<T> list = this.getHibernateTemplate().findByCriteria(
                    detachedCriteria);
            return list;
        } catch (RuntimeException re) {
            //log.error("find by property name failed", re);
            throw re;
        }
    }

    @SuppressWarnings("unchecked")
    public List<T> FindAll() {
        return getHibernateTemplate().loadAll(entityClass);
    }

    @SuppressWarnings("unchecked")
    public PageInfo FindAllByPage(int statIndex, int pageSize) {
        try {
            DetachedCriteria detachedCriteria = DetachedCriteria.forClass(this.entityClass);
            PageInfo pageInfo = findPageByCriteria(detachedCriteria, statIndex, pageSize);
            return pageInfo;
        } catch (RuntimeException re) {
            //log.error("find by property name failed", re);
            throw re;
        }
    }

    @SuppressWarnings("unchecked")
    public List<T> findByCriteria(DetachedCriteria criteria) {
        try {
            criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
            List<T> results = getHibernateTemplate().findByCriteria(criteria);
            return results;
        } catch (RuntimeException re) {
            throw re;
        }
    }

    public PageInfo findPageByCriteria(DetachedCriteria criteria,
            int startIndex, int pageSize) {
//        criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
        int totalCount = getHibernateTemplate().findByCriteria(criteria).size();
        List<T> objects = findByCriteria(criteria, startIndex, pageSize);
        PageInfo pageInfo = new PageInfo(objects, pageSize, startIndex, totalCount);
        return pageInfo;
    }

    @SuppressWarnings("unchecked")
    private List<T> findByCriteria(DetachedCriteria criteria,
            int startIndex, int pageSize) {
        try {
            List<T> results = getHibernateTemplate().findByCriteria(criteria, startIndex, pageSize);
            return results;
        } catch (RuntimeException re) {
            throw re;
        }
    }
}
